package org.finegold.core.netty.config;

import cn.hutool.core.thread.ThreadUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.finegold.core.launch.props.FinePropertySource;
import org.finegold.core.netty.core.codc.NettyServerInitializer;
import org.finegold.core.netty.core.websocket.WebSocketServerInitializer;
import org.finegold.core.netty.props.ServerChannelProperties;
import org.finegold.core.netty.props.ServerChildChannelProperties;
import org.finegold.core.netty.props.ServerNormalProperties;
import org.finegold.core.netty.props.ServerProperties;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;


/**
 * netty配置类
 *
 * @author QinXueFeng
 */
@Slf4j
@AllArgsConstructor
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ServerChannelProperties.class,
	ServerChildChannelProperties.class, ServerNormalProperties.class, ServerProperties.class})
@ConditionalOnProperty(value = "fine.netty.enabled", havingValue = "true")
public class NettyConfiguration implements InitializingBean, DisposableBean {

	private final ServerChannelProperties serverChannelProperties;
	private final ServerChildChannelProperties serverChildChannelProperties;
	private final ServerNormalProperties serverNormalProperties;
	private final ServerProperties serverProperties;

	//Acceptor连接线程组
	final EventLoopGroup parent = new NioEventLoopGroup();
	//主工作处理IO线程组
	final EventLoopGroup worker = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2);

	@Override
	public void destroy(){
		log.info("Netty Server Destroy!");
		parent.shutdownGracefully();
		worker.shutdownGracefully();
	}

	@Override
	public void afterPropertiesSet() {
		ThreadUtil.execute(() -> {
			try {
				//创建引导类
				ServerBootstrap sb = new ServerBootstrap();
				sb.group(parent, worker).//加入主从多线程模型
					channel(NioServerSocketChannel.class).
					option(ChannelOption.CONNECT_TIMEOUT_MILLIS, serverNormalProperties.getConnectTimeoutMillis()).
					option(ChannelOption.AUTO_READ, serverNormalProperties.getAutoRead()).
					option(ChannelOption.WRITE_BUFFER_WATER_MARK, serverNormalProperties.getWriteBufferWaterMark()).
					option(ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP, serverNormalProperties.getSingleEventexecutorPerGroup()).
					option(ChannelOption.SO_RCVBUF, serverChannelProperties.getSoRcvbuf()).
					option(ChannelOption.SO_REUSEADDR, serverChannelProperties.getSoReuseaddr()).
					option(ChannelOption.SO_BACKLOG, serverChannelProperties.getSoBacklog()).
					childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, serverNormalProperties.getConnectTimeoutMillis()).
					childOption(ChannelOption.AUTO_READ, serverNormalProperties.getAutoRead()).
					childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, serverNormalProperties.getWriteBufferWaterMark()).
					childOption(ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP, serverNormalProperties.getSingleEventexecutorPerGroup()).
					childOption(ChannelOption.SO_RCVBUF, serverChildChannelProperties.getSoRcvbuf()).
					childOption(ChannelOption.SO_SNDBUF, serverChildChannelProperties.getSoSndbuf()).
					childOption(ChannelOption.TCP_NODELAY, serverChildChannelProperties.getTcpNodelay()).
					childOption(ChannelOption.SO_KEEPALIVE, serverChildChannelProperties.getSoKeepalive()).
					childOption(ChannelOption.SO_REUSEADDR, serverChildChannelProperties.getSoReuseaddr()).
					childHandler(serverProperties.getType()==1?new NettyServerInitializer(serverProperties):new WebSocketServerInitializer(serverProperties));//初始化处理器
				//同步启动并监听
				ChannelFuture future = sb.bind(serverProperties.getPort()).sync();
				log.info("NettyServer Start Bind Port: {}", serverProperties.getPort());
				//关闭事件
				future.channel().closeFuture().sync();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				parent.shutdownGracefully();
				worker.shutdownGracefully();
			}
		});
	}
}
